package com.group01.community.utils;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

/**
 * @author iuoly
 * @date 2022/6/28
 */
public class RsaUtil {

    /**
     * 密钥
     */
    public static final String RSA_SECRET = "zxcvbnm";

    /**
     * 密钥存储路径
     */
    //public static final String RSA_PATH = System.getProperty("user.dir")+ "/src/main/resources/rsa/";
    public static final String RSA_PATH = System.getProperty("user.dir")+ "/rsa/";

    /**
     * 私钥生成路径
     */
    public static final String RSA_PRI_KEY_PATH = RSA_PATH + "priKey.rsa";

    /**
     * 公钥生成路径
     */
    public static final String RSA_PUB_KEY_PATH = RSA_PATH + "pubKey.rsa";

    public static PrivateKey privateKey;
    public static PublicKey publicKey;

    /*
     * 类加载时，生成公钥和私钥文件
     */
    static {
        try {
            File rsaDir = new File(RSA_PATH);
            rsaDir.mkdirs();
            File priKey = new File(RSA_PRI_KEY_PATH);
            File pubKey = new File(RSA_PUB_KEY_PATH);

            if (!priKey.exists() || !pubKey.exists()) {
                RsaUtil.generateKey(RSA_PUB_KEY_PATH, RSA_PRI_KEY_PATH, RSA_SECRET);
            }
            privateKey = RsaUtil.getPrivateKey(RSA_PRI_KEY_PATH);
            publicKey = RsaUtil.getPublicKey(RSA_PUB_KEY_PATH);
        } catch (NoSuchAlgorithmException | IOException | InvalidKeySpecException e) {
            e.printStackTrace();
        }
    }

    /**
     * 从文件中读取密钥
     * @param filename 私钥的文件名
     * @return
     * @throws InvalidKeySpecException
     * @throws NoSuchAlgorithmException
     * @throws IOException
     */
    public static PrivateKey getPrivateKey(String filename) throws InvalidKeySpecException, NoSuchAlgorithmException, IOException {
        byte[] bytes = readFile(filename);
        return getPrivateKey(bytes);
    }

    /**
     * 获取密钥
     * @param bytes 私钥的字节形式
     * @return
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     */
    private static PrivateKey getPrivateKey(byte[] bytes) throws NoSuchAlgorithmException, InvalidKeySpecException {
        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(bytes);
        KeyFactory factory = KeyFactory.getInstance("RSA");
        return factory.generatePrivate(spec);
    }

    /**
     * 从文件中读取公钥
     * @param filename 公钥的文件名
     * @return
     * @throws IOException
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     */
    public static PublicKey getPublicKey(String filename) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
        byte[] bytes = readFile(filename);
        return getPublicKey(bytes);
    }

    /**
     * 获取公钥
     * @param bytes 公钥的字节形式
     * @return
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     */
    private static PublicKey getPublicKey(byte[] bytes) throws NoSuchAlgorithmException, InvalidKeySpecException {
        X509EncodedKeySpec spec = new X509EncodedKeySpec(bytes);
        KeyFactory factory = KeyFactory.getInstance("RSA");
        return factory.generatePublic(spec);
    }

    /**
     * 根据密文，生成公钥和私钥并写入到文件中
     * @param publicKeyFilename 公钥路径名
     * @param privateKeyFilename 私钥路径名
     * @param secret 密文
     * @throws NoSuchAlgorithmException
     * @throws IOException
     */
    public static void generateKey(String publicKeyFilename, String privateKeyFilename, String secret) throws NoSuchAlgorithmException, IOException {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        SecureRandom secureRandom = new SecureRandom(secret.getBytes());
        keyPairGenerator.initialize(1024, secureRandom);
        KeyPair keyPair = keyPairGenerator.genKeyPair();

        /*
          获取公钥并写出
         */
        byte[] pubKeyBytes = keyPair.getPublic().getEncoded();
        writeFile(publicKeyFilename, pubKeyBytes);

        /*
          获取私钥并写出
         */
        byte[] priKeyBytes = keyPair.getPrivate().getEncoded();
        writeFile(privateKeyFilename, priKeyBytes);
    }

    private static byte[] readFile(String filename) throws IOException {
        return Files.readAllBytes(new File(filename).toPath());
    }

    private static void writeFile(String destPath, byte[] bytes) throws IOException {
        File dest = new File(destPath);
        if (!dest.exists()) {
            dest.createNewFile();
        }
        Files.write(dest.toPath(), bytes);
    }

}
